#pragma once 

#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <math.h>
#define NUM 5
class Task{
  public:
    int base;
  public:
    Task(int _b):base(_b){

    }

    void Run()
    {
      std::cout << "run" << base << pow(base, 2) <<std::endl;
    }
};

class ThreadPool{
  private:
    std::queue<Task*> q;
    int max_num;
    pthread_mutex_t lock;
    pthread_cond_t cond;
  private:
    void LockQueue()
    {
      pthread_mutex_lock(&lock);
    }
    void UnlockQueue()
    {
      pthread_mutex_unlock(&lock);
    }
    bool IsEmpty()
    {
      return q.size() == 0;
    }
    void ThreadWait()
    {
      pthread_cond_wait(&cond, &lock);
    }
    void ThreadWakeup()
    {
      pthread_cond_signal(&cond);
    }
  public:
    ThreadPool(int _max = NUM):max_num(_max)
    {
      
    }
    static void* Routine(void* arg)
    {
      ThreadPool* this_p = (ThreadPool*)arg;
      while(true){
        this_p->LockQueue();
        while(this_p->IsEmpty()){
          this_p->ThreadWait();
        }

        Task t;
        this_p->Get(t);
        this_p->UnlockQueue();
        t.Run();
      }
    }

    void ThreadPoolInit()
    {
      pthread_mutex_init(&lock,nullptr);
      pthread_cond_init(&cond,nullptr);
      pthread_t t;
      for(int i = 0; i < max_num; i++){
        pthread_create(&t, nullptr, Routine, this);
      }
    }

    void Put(Task &in)
    {
      LockQueue();
      q.push(&in);
      UnlockQueue();

      ThreadWakeup();
    }

    void Get(Task &out)
    {
      Task* t = q.front();
      q.pop();
      out = *t;

    }

    ~ThreadPool()
    {
      pthread_mutex_destroy(&lock);
      pthread_cond_destroy(&cond);
    }
};
